package com.warkiz.widget;

import ohos.agp.colors.RgbColor;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Text;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;

/**
 * created by zhuangguangquan on 2017/9/9
 * <p>
 * https://github.com/warkiz/IndicatorSeekBar
 * <p>
 * Donation/打赏:
 * If this library is helpful to you ,you can give me a donation by:
 *
 * @see <a href="https://www.paypal.me/BuyMeACupOfTeaThx">ZhuanGuangQuan's Paypal</a>, or
 * @see <a href="https://github.com/warkiz/IndicatorSeekBar/blob/master/app/src/main/res/mipmap-xxhdpi/wechat_pay.png?raw=true">微信支付</a>, or
 * @see <a href="https://github.com/warkiz/IndicatorSeekBar/blob/master/app/src/main/res/mipmap-xxhdpi/alipay.png?raw=true">支付宝</a>
 * <p>
 */
public class Indicator {
    private int[] mLocation = new int[2];
    private ArrowView mArrowView;
    private Text mProgressTextView;
    private MyPopup mIndicatorPopW;
    private DirectionalLayout mTopContentView;
    private Color mIndicatorColor;
    private Context mContext;
    private int mIndicatorType;
    private IndicatorSeekBar mSeekBar;
    private Component mIndicatorView;
    private Component mIndicatorCustomView;
    private Component mIndicatorCustomTopContentView;
    private int mIndicatorTextSize;
    private Color mIndicatorTextColor;

    public Indicator(Context context,
                     IndicatorSeekBar seekBar,
                     Color indicatorColor,
                     int indicatorType,
                     int indicatorTextSize,
                     Color indicatorTextColor,
                     Component indicatorCustomView,
                     Component indicatorCustomTopContentView) {
        this.mContext = context;
        this.mSeekBar = seekBar;
        this.mIndicatorColor = indicatorColor;
        this.mIndicatorType = indicatorType;
        this.mIndicatorCustomView = indicatorCustomView;
        this.mIndicatorCustomTopContentView = indicatorCustomTopContentView;
        this.mIndicatorTextSize = indicatorTextSize;
        this.mIndicatorTextColor = indicatorTextColor;

        initIndicator();
    }

    public void initIndicator() {
        if (mIndicatorType == IndicatorType.CUSTOM) {
            if (mIndicatorCustomView != null) {
                mIndicatorView = mIndicatorCustomView;
                // for the custom indicator view, if progress need to show when seeking ,
                // need a TextView to show progress and this textView 's identify must be progress;
                int progressTextViewId = ResourceTable.Id_isb_progress;
                if (progressTextViewId > 0) {
                    Component view = mIndicatorView.findComponentById(progressTextViewId);
                    if (view != null) {
                        if (view instanceof Text) {
                            //progressText
                            mProgressTextView = (Text) view;
                            mProgressTextView.setText(mSeekBar.getIndicatorTextString());
                            mProgressTextView.setTextSize(mIndicatorTextSize);
                            mProgressTextView.setTextColor(mIndicatorTextColor);
                        } else {
                            throw new ClassCastException("the view identified by isb_progress in indicator custom layout can not be cast to TextView");
                        }
                    }
                }
            } else {
                throw new IllegalArgumentException("the attr：indicator_custom_layout must be set while you set the indicator type to CUSTOM.");
            }
        } else {
            if (mIndicatorType == IndicatorType.CIRCULAR_BUBBLE) {
                mIndicatorView = new CircleBubbleView(mContext, mIndicatorTextSize, mIndicatorTextColor, mIndicatorColor, "1000");
                ((CircleBubbleView) mIndicatorView).setProgress(mSeekBar.getIndicatorTextString());
            } else {
                mIndicatorView = LayoutScatter.getInstance(mContext).parse(ResourceTable.Layout_isb_indicator, null, false);
                mIndicatorView.setLayoutConfig(new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_CONTENT, ComponentContainer.LayoutConfig.MATCH_CONTENT));
                //container
                mTopContentView = (DirectionalLayout) mIndicatorView.findComponentById(ResourceTable.Id_indicator_container);
                //arrow
                mArrowView = (ArrowView) mIndicatorView.findComponentById(ResourceTable.Id_indicator_arrow);
                mArrowView.setColor(mIndicatorColor.getValue());
                //progressText
                mProgressTextView = (Text) mIndicatorView.findComponentById(ResourceTable.Id_isb_progress);
                mProgressTextView.setText(mSeekBar.getIndicatorTextString());
                mProgressTextView.setTextSize(mIndicatorTextSize);
                mProgressTextView.setTextColor(mIndicatorTextColor);
                mTopContentView.setBackground(getGradientDrawable());
                mTopContentView.setLayoutConfig(new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_CONTENT, ComponentContainer.LayoutConfig.MATCH_CONTENT));
                //custom top content view
                if (mIndicatorCustomTopContentView != null) {
                    //for the custom indicator top content view, if progress need to show when seeking ,
                    //need a TextView to show progress and this textView 's identify must be progress;
                    int progressTextViewId = ResourceTable.Id_isb_progress;
                    Component topContentView = mIndicatorCustomTopContentView;
                    if (progressTextViewId > 0) {
                        Component tv = topContentView.findComponentById(progressTextViewId);
                        if (tv != null && tv instanceof Text) {
                            setTopContentView(topContentView, (Text) tv);
                        } else {
                            setTopContentView(topContentView);
                        }
                    } else {
                        setTopContentView(topContentView);
                    }

                }
            }
        }
    }

    private ShapeElement getGradientDrawable() {
        ShapeElement tvDrawable;
        if (mIndicatorType == IndicatorType.ROUNDED_RECTANGLE) {
            tvDrawable = new ShapeElement(mContext, ResourceTable.Graphic_isb_indicator_rounded_corners);
        } else {
            tvDrawable = new ShapeElement(mContext, ResourceTable.Graphic_isb_indicator_square_corners);
        }
        tvDrawable.setRgbColor(RgbColor.fromArgbInt(mIndicatorColor.getValue()));
        return tvDrawable;
    }

    private int getWindowWidth() {
        Display display = DisplayManager.getInstance().getDefaultDisplay(mContext).get();
        return display.getAttributes().width;
    }

    private int getIndicatorScreenX() {
        mLocation = mSeekBar.getLocationOnScreen();
        return mLocation[0];
    }

    private void adjustArrow(float touchX) {
        if (mIndicatorType == IndicatorType.CUSTOM || mIndicatorType == IndicatorType.CIRCULAR_BUBBLE) {
            return;
        }
        int indicatorScreenX = getIndicatorScreenX();

        if (indicatorScreenX + (int) touchX < mIndicatorView.getEstimatedWidth() / 2) {
            setMargin(mArrowView, -(int) (mIndicatorView.getEstimatedWidth() / 2 - indicatorScreenX - (int) touchX), -1, -1, -1);
        } else if (getWindowWidth() - indicatorScreenX - (int) touchX < mIndicatorView.getEstimatedWidth() / 2) {
            setMargin(mArrowView, (int) (mIndicatorView.getEstimatedWidth() / 2 - (getWindowWidth() - indicatorScreenX - (int) touchX)), -1, -1, -1);
        } else {
            setMargin(mArrowView, 0, 0, 0, 0);
        }
    }

    private void setMargin(Component view, int left, int top, int right, int bottom) {
        if (view == null) {
            return;
        }
        ComponentContainer.LayoutConfig layoutConfig = view.getLayoutConfig();
        layoutConfig.setMargins(left == -1 ? layoutConfig.getMarginLeft() : left,
            top == -1 ? layoutConfig.getMarginTop() : top,
            right == -1 ? layoutConfig.getMarginRight() : right,
            bottom == -1 ? layoutConfig.getMarginBottom() : bottom);
        view.setLayoutConfig(layoutConfig);
        view.postLayout();
    }

    void iniPop() {
        if (mIndicatorPopW != null) {
            return;
        }
        if (mIndicatorType != IndicatorType.NONE && mIndicatorView != null) {
            mIndicatorView.estimateSize(0, 0);
            mIndicatorPopW = new MyPopup(mContext, mSeekBar, mIndicatorView);
        }
    }

    Component getInsideContentView() {
        return mIndicatorView;
    }

    void setProgressTextView(String text) {
        if (mIndicatorView instanceof CircleBubbleView) {
            ((CircleBubbleView) mIndicatorView).setProgress(text);
        } else if (mProgressTextView != null) {
            mProgressTextView.setText(text);
        }
    }

    void updateIndicatorLocation(int offset) {
        setMargin(mIndicatorView, offset, -1, -1, -1);
    }

    void updateArrowViewLocation(int offset) {
        setMargin(mArrowView, offset, -1, -1, -1);
    }

    /**
     * update the indicator position
     *
     * @param touchX the x location you touch without padding left.
     */
    void update(float touchX) {
        if (!mSeekBar.isEnabled() || !(mSeekBar.getVisibility() == Component.VISIBLE)) {
            return;
        }
        refreshProgressText();
        if (mIndicatorPopW != null) {
            mIndicatorPopW.getContentView().estimateSize(0, 0);
            mIndicatorPopW.update(mSeekBar, touchX + mSeekBar.getMarginLeft());
            adjustArrow(touchX);
        }
    }

    /**
     * call this method to show the indicator.
     *
     * @param touchX the x location you touch, padding left excluded.
     */
    void show(float touchX) {
        if (!mSeekBar.isEnabled() || !(mSeekBar.getVisibility() == Component.VISIBLE)) {
            return;
        }
        refreshProgressText();
        if (mIndicatorPopW != null) {
            mIndicatorPopW.getContentView().estimateSize(0, 0);
            mIndicatorPopW.show(mSeekBar, touchX + mSeekBar.getMarginLeft());
            adjustArrow(touchX);
        }
    }

    void refreshProgressText() {
        String tickTextString = mSeekBar.getIndicatorTextString();
        if (mIndicatorView instanceof CircleBubbleView) {
            ((CircleBubbleView) mIndicatorView).setProgress(tickTextString);
        } else if (mProgressTextView != null) {
            mProgressTextView.setText(tickTextString);
        }
    }

    /**
     * call this method hide the indicator
     */
    void hide() {
        if (mIndicatorPopW == null) {
            return;
        }
        mIndicatorPopW.hide();
    }

    boolean isShowing() {
        return mIndicatorPopW != null && mIndicatorPopW.isShowing();
    }


    /*----------------------API START-------------------*/

    /**
     * get the indicator content view.
     *
     * @return the view which is inside indicator.
     */
    public Component getContentView() {
        return mIndicatorView;
    }

    /**
     * call this method to replace the current indicator with a new indicator view , indicator arrow will be replace ,too.
     *
     * @param customIndicatorView a new content view for indicator.
     */
    public void setContentView(Component customIndicatorView) {
        this.mIndicatorType = IndicatorType.CUSTOM;
        this.mIndicatorCustomView = customIndicatorView;
        initIndicator();
    }

    /**
     * call this method to replace the current indicator with a new indicator view, indicator arrow will be replace ,too.
     *
     * @param customIndicatorView a new content view for indicator.
     * @param progressTextView this TextView will show the progress or tick text, must be found in @param customIndicatorView
     */
    public void setContentView(Component customIndicatorView, Text progressTextView) {
        this.mProgressTextView = progressTextView;
        this.mIndicatorType = IndicatorType.CUSTOM;
        this.mIndicatorCustomView = customIndicatorView;
        initIndicator();
    }

    /**
     * get the indicator top content view.
     * if indicator type {@link } is CUSTOM or CIRCULAR_BUBBLE, call this method will get a null value.
     *
     * @return the view which is inside indicator's top part, not include arrow
     */
    public ComponentContainer getTopContentView() {
        return mTopContentView;
    }

    /**
     * set the View to the indicator top container, not influence indicator arrow ;
     * if indicator type {@link } is CUSTOM or CIRCULAR_BUBBLE, call this method will be not worked.
     *
     * @param topContentView the view is inside the indicator TOP part, not influence indicator arrow;
     */
    public void setTopContentView(Component topContentView) {
        setTopContentView(topContentView, null);
    }

    /**
     * set the  View to the indicator top container, and show the changing progress in indicator when seek;
     * not influence indicator arrow;
     * if indicator type is custom , this method will be not work.
     *
     * @param topContentView the view is inside the indicator TOP part, not influence indicator arrow;
     * @param progressTextView this TextView will show the progress or tick text, must be found in @param topContentView
     */
    public void setTopContentView(Component topContentView, Text progressTextView) {
        this.mProgressTextView = progressTextView;
        this.mTopContentView.removeAllComponents();
        topContentView.setBackground(getGradientDrawable());
        this.mTopContentView.addComponent(topContentView);
    }

    public void invalidate() {
        Component contentView = getContentView();
        if (contentView != null) {
            getContentView().invalidate();
        }
        if (mArrowView != null) {
            mArrowView.invalidate();
        }
    }

    public void setIndicatorType(int indicatorType) {
        this.mIndicatorType = indicatorType;
    }

    public void setIndicatorContentView(Component contentView) {
        this.mIndicatorCustomView = contentView;
    }

    /*----------------------API END-------------------*/

}